/**
 * generator：Generator 函数是 ES6 提供的一种异步编程解决方案
 * 语法上：可以理解为一个状态机（封装了多个内部状态）
 * 执行上：可以返回一个遍历器对象（可以依次遍历Generator函数内部的每一个状态）
 * 形式上：function和函数名之间有一个 * 号（ES6没有规定这个*号写在那个位置），函数体内部使用 yield 产生不同内部状态
 *
 * 调用 Generator 函数后，该函数并不执行,返回的也不是函数运行结果 而是一个指向内部状态的指针对象，
 */

function* helloWorldGenerator() {
  // 三个状态
  yield 'hello'
  yield 'world'
  return 'ending'
}

// 与普通函数不同，返回的是一个遍历器对象
let hw = helloWorldGenerator() //Object [Generator] {}
console.log(hw)

// 调用遍历器对象的 next 方法，使得指针移向下一个状态。 value是当前yield的值，done：false（遍历未结束）
console.log(hw.next())  // { value: 'hello', done: false }
console.log(hw.next())  // { value: 'world', done: false }
console.log(hw.next())  // { value: 'ending', done: true }
console.log(hw.next())  // { value: undefined, done: true }

/**
 * yield : 暂停标志
 * 遇到yield就会暂停执行后面的操作，遇到next就继续往下执行
 */
// 数组扁平化--实现flat
let flat = function* (arr) {
  for (let i = 0; i < arr.length; i++) {
    if (typeof arr[i] !== 'number') {
      // 递归
      yield* flat(arr[i]) // yield＊ 语句，用来在一个 Generator 函数里面执行另外一个 Generator 函数。
    } else {
      yield arr[i]
    }
  }
}
let arr = [1, 2, [3, 4, [5, 6, 7]]]

for (let item of flat(arr)) {
  console.log(item)
}

/**
 * 由于 Generator 函数就是遍历器生成函数，因此可以把 Generator 赋值给对象的
 * Symbol iterator 属性，从而使得该对象具有 Iterator 接口
 */

let person = {
  name: 'zhangsan',
  age: 20,
  arr: [1, 2, 3, 4],
  // 如果一个对象的属性是 Generator 函数 也可以写成下面形式：
  // *[Symbol.iterator](){ }
  [Symbol.iterator]: function* () {
    let keys = Object.keys(this)
    for (let i = 0; i < keys.length; i++) {
      yield {
        key: keys[i],
        value: this[keys[i]]
      }
    }
  }
}
for (let {key, value} of person) {
  console.log(key, value)
}
/*
name zhangsan
age 20
arr [ 1, 2, 3, 4 ] */

/**
 * Generator 函数是 ES6 对协程的实现，但属于不完全实现
 * Generator 可以暂停函数执行，返回任意表达式的值。
 */


/**
 * Generator 的异步编程应用
 * 传统方法：回调函数、事件监听、发布/订阅、promise对象
 * 回调地狱（callback hell）：多重嵌套、代码纵向发展、无法管理、多个异步操作形成强耦合（只要有一个操作需要修改，它的上层回调函数和下层回调函数就都要跟着修改。）
 * Promise的缺点：代码冗余、then的堆积
 * 协程：coroutine 多个线程相互协作，完成异步任务。Generator 函数是协程在 ES6 中的实现，最大特点就是可以交出函数的执行权（即暂停执行）。
 * next 方法的作用是分阶段执行 Generator 函数。
 */

// 异步任务的封装
// const fetch = require('node-fetch')

/*function* gen() {
  let url = 'http://junsonjack.cn:8888/api/v2/blogs'
  let result = yield fetch(url)
  console.log(result.json())
}

let g = gen()
console.log(g.next())*/

// Thunk 函数：Thunk 函数是自动执行 Generator 函数的一种方法。
function f(a, b) {
  console.log(a + b)
  return a + b
}

let x = 1
f(x, 2)  // 3